package com.project.restaurant.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.project.restaurant.common.api.ResponseResult;
import com.project.restaurant.common.api.ServiceReturn;
import com.project.restaurant.common.api.ServiceStateCode;
import com.project.restaurant.common.dao.BriefRestaurantDao;
import com.project.restaurant.common.dao.RestaurantDetailInfoDao;
import com.project.restaurant.common.dto.BriefRestaurant;
import com.project.restaurant.common.exception.ServiceException;
import com.project.restaurant.common.key.SessionAttribute;
import com.project.restaurant.common.util.CachedThreadPoolUtil;
import com.project.restaurant.common.util.SpringUtil;
import com.project.restaurant.order.dto.QRPayInfo;
import com.project.restaurant.common.dto.RestaurantDetailInfo;
import com.project.restaurant.mapper.MealMapper;
import com.project.restaurant.mapper.OrderDetailItemMapper;
import com.project.restaurant.mapper.OrderMapper;
import com.project.restaurant.order.dao.BriefOrderDao;
import com.project.restaurant.order.dao.DetailOrderDao;
import com.project.restaurant.order.dto.*;
import com.project.restaurant.order.enums.OrderState;
import com.project.restaurant.order.feign.WXNativePayService;
import com.project.restaurant.order.listener.event.QRPaySuccessEvent;
import com.project.restaurant.order.listener.event.QRPayTimeEvent;
import com.project.restaurant.order.service.OrderService;
import com.project.restaurant.order.util.GenerateCodeUtil;
import com.project.restaurant.pojo.Meal;
import com.project.restaurant.pojo.Order;
import com.project.restaurant.pojo.OrderDetailItem;
import com.project.restaurant.pojo.User;
import com.project.restaurant.redis.key.RedisKey;
import com.project.restaurant.redis.service.RedisClient;
import com.project.restaurant.thirdpart.dto.LookPayResult;
import com.project.restaurant.thirdpart.dto.WXNativePayOrder;
import com.project.restaurant.thirdpart.enums.WXPayState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    BriefOrderDao briefOrderDao;
    @Autowired
    DetailOrderDao detailOrderDao;
    @Autowired
    BriefRestaurantDao briefRestaurantDao;
    @Autowired
    MealMapper mealMapper;
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    OrderDetailItemMapper orderDetailItemMapper;
    @Autowired
    RestaurantDetailInfoDao restaurantDetailInfoDao;
    @Autowired
    WXNativePayService wxNativePayService;
    @Autowired
    RedisClient redisClient;
    @Autowired
    ApplicationEventPublisher publisher;

    @Override
    public BriefOrderVo obtainUserBriefOrder(OrderState orderState) {
        User user = (User) SpringUtil.getHttpSession().getAttribute(SessionAttribute.NOW_LOGIN_USER);
        List<BriefOrder> briefOrderList = briefOrderDao.selectByUserId(new OrderQueryParam(user.getUserId(),orderState));
        BriefRestaurant briefRestaurant = briefRestaurantDao.defaultSelect();
        return new BriefOrderVo(briefRestaurant,briefOrderList);
    }

    @Override
    public DetailOrderVo obtainUserDetailOrder(String orderId) {
        User user = (User) SpringUtil.getHttpSession().getAttribute(SessionAttribute.NOW_LOGIN_USER);
        DetailOrder detailOrder = detailOrderDao.selectById(orderId,user.getUserId());
        BriefRestaurant briefRestaurant = briefRestaurantDao.defaultSelect();
        return new DetailOrderVo(briefRestaurant,detailOrder);
    }

    @Transactional
    @Override
    public QRPayInfo createOrder(PlaceOrderInfo placeOrderInfo) throws Exception {

        LambdaQueryWrapper<Meal> mealQuery = new LambdaQueryWrapper<Meal>();
        HashMap<Integer,Integer> mealInventoryMap = new HashMap<>();
        for(PlaceOrderInfo.PlaceMealItem item: placeOrderInfo.getMealItems()){
            Integer inventory = mealInventoryMap.getOrDefault(item.getMealId(),0);
            mealInventoryMap.put(item.getMealId(),inventory + item.getPurchaseNum());
        }
        mealQuery.in(Meal::getMealId,mealInventoryMap.keySet());
        List<Meal> meals = mealMapper.selectList(mealQuery);
        //判断当前购买的所有餐点是否存在
        if (mealInventoryMap.size() != meals.size()) {
            throw new ServiceException(ServiceStateCode.NOT_EXIST_MEAL);
        }
        //判断当前购买的餐点库存是否足够
        for(Meal meal : meals){
            if(meal.getMealInventory() < mealInventoryMap.get(meal.getMealId())){
                throw new ServiceException(ServiceStateCode.INVENTORY_LACK);
            }
        }

        //计算订单里的总价格
        int totalPrice=0;
        for(Meal meal:meals){
            totalPrice += meal.getMealPrice() * mealInventoryMap.get(meal.getMealId());
        }
        //创建新的订单
        Order order = placeOrderInfo.getOrder();
        order.setTotalPrice(totalPrice);
        //将订单插入到数据库中,返回插入的订单id赋值给order的orderId。或者使用order.save()后的order.getOrderId()来插入的订单id
        orderMapper.insertOne(order);
        String orderId = order.getOrderId();
        CachedThreadPoolUtil.submit(()->{
            //获取订单里的各个订单项
            List<OrderDetailItem> orderItems = placeOrderInfo.getOrderItems(orderId, meals);
            //将订单项插入到数据库中
            orderDetailItemMapper.insertList(orderItems);
        });
        RestaurantDetailInfo restaurantInfo = restaurantDetailInfoDao.defaultSelect();

        //微信支付那边默认过期时间是30分钟
        Instant timeExpireInstant = LocalDateTime.now().toInstant(ZoneOffset.ofHoursMinutes(-8, -30));
        LocalDateTime timeExpire = LocalDateTime.ofInstant(timeExpireInstant,ZoneOffset.ofHours(0));
        WXNativePayOrder wxNativePayOrder = WXNativePayOrder.getWXNativePayOrder(order, meals.get(0), restaurantInfo, timeExpire);
        //生成支付url
        ResponseResult<String> responseResult = wxNativePayService.generateNativePayUrl(wxNativePayOrder);
        if( ServiceStateCode.SUCCESS.getCode()==responseResult.getCode() ){
            try {
                String payUrl = responseResult.getData();
                //将支付url生成二维码
                String qrCode = GenerateCodeUtil.generateQRCode(payUrl, 300, 300).toStrBase64();
                QRPayInfo qrPayInfo = new QRPayInfo(orderId, qrCode);
                //发布该监听该支付订单时间。即监听该订单是否支付超时，如果超时则取消该订单，否则完成订单支付流程
                publisher.publishEvent(new QRPayTimeEvent(qrPayInfo));
                redisClient.setEx(RedisKey.ORDER_PAY_URL+orderId,payUrl,1800);
                return qrPayInfo;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        throw new ServiceException(ServiceStateCode.FAILURE);
    }

    @Override
    public String flushQRCode(String orderId) {
        String payUrl = (String) redisClient.get(RedisKey.ORDER_PAY_URL + orderId);
        if(payUrl !=null){
            String qrCode;
            try {
                qrCode = GenerateCodeUtil.generateQRCode(payUrl+"&"+new Date().getTime(), 300, 300).toStrBase64();
                return qrCode;
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException(ServiceStateCode.FAILURE);
            }
        }
        throw new ServiceException(ServiceStateCode.ORDER_PAY_ALREADY_TIMEOUT);
    }

    @Override
    public WXPayState isWXPaySuccess(String orderId) {
        boolean exist = redisClient.existKey(RedisKey.ORDER_PAY_URL + orderId);
        if(!exist){
            throw new ServiceException(ServiceStateCode.NOT_EXIST_PAY_ORDER);
        }
        ResponseResult<LookPayResult> responseResult = wxNativePayService.lookOrderPayState(orderId);
        //如果响应成功，则
        if(responseResult.getCode() == ServiceStateCode.SUCCESS.getCode()){
            LookPayResult data = responseResult.getData();
            WXPayState payState = data.getPayState();
            //如果支付成功，则
            if (WXPayState.SUCCESS.equals(payState)) {
                //支付成功后，只调用一次，即防止多次调用
                CachedThreadPoolUtil.submit(() -> {
                    Order updateOrder = new Order()
                            .setOrderId(orderId)
                            .setTransactionId(data.getTransactionId())
                            .setOrderState(OrderState.ALREADY_PAY.getOrderState());
                    //更新订单状态与支付凭证
                    orderMapper.updateNotNull(updateOrder);
                    //发布支付成功事件
                    publisher.publishEvent(new QRPaySuccessEvent(orderId));
                });
            }
            return payState;
        }
        throw  new ServiceException(ServiceStateCode.FAILURE);
    }
}
